/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/archive/xml_iarchive.hpp>

#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;

#include <algorithm>
#include <cfloat>

#include "map.h"
#include "map_manager.h"
#include "application.h"
#include "model.h"
#include "unique_name_generator.h"
#include "windows_manager_serialization.h"
#include "action.h"

class LoadWindowsManagerRoutine : public wxThread
{
public:
    WindowsManager* mgr;
    wxWindow* pane;
    std::string filename;

    ExitCode Entry()
    {
        boost::filesystem::ifstream ifs(boost::filesystem::path("./layout.xml"));

        if( !ifs.good())
        {
            wxLogError(_("Impossible de charger layout.xml, la configuration par défaut (fichier editor_layout) de la disposition sera utilisé."));
            mgr->readConfigurationFromFile(wxT("editor_layout"));
        }
        else
        {
            try
            {
                boost::archive::xml_iarchive ia(ifs);
                try
                {
                    ia >> make_nvp("sys",*mgr);
                }
                catch(...)
                {
                    std::cout << "Interruption, le fichier layout.xml possède des erreurs, veuillez utiliser l’option -D de la ligne de commande pour réinitialiser les paramètres par défaut";
                    return false;
                }
            }
            catch(...)
            {
                typedef bool (WindowsManager::*func)(const wxString&);
                appInstance->callFunction( bind( (func)&WindowsManager::readConfigurationFromFile, mgr, wxT("editor_layout")));
            }

            ifs.close();
        }

        if( !filename.length())
            filename = "Nouvelle carte";
        appInstance->callFunction( bind( &wxAuiNotebook::AddPage, appInstance->m_frame->m_auiNotebook,pane, wxString(filename.c_str(), wxConvUTF8),false, wxNullBitmap));
        appInstance->callFunction( bind( &wxAuiNotebook::SetSelection,appInstance->m_frame->m_auiNotebook, bind(&wxAuiNotebook::GetPageIndex,appInstance->m_frame->m_auiNotebook,pane)));
        return 0;
    }
};

Map::Map()
{
    modelManager = new ModelManager(this);

    actionMgr = new ActionManager;
    mgr = NULL;
}

Map::~Map()
{
    if( mgr)
        delete mgr;
    delete actionMgr;
    mapManagerInstance.m_mapList.erase(remove(mapManagerInstance.m_mapList.begin(), mapManagerInstance.m_mapList.end(), this), mapManagerInstance.m_mapList.end());
}

bool Map::printMessageAndEventuallyDestroy(bool asCancel)
{
    wxString f = wxString(filename.c_str(),wxConvUTF8);
    if( !f.length())
        f = _("Nouvelle carte");

    wxMessageDialog* msg = new wxMessageDialog( appInstance->m_frame,
                                                wxString(_("Voulez-vous enregistrer les modifications faites à ")) + f + wxString(_(" avant de quitter.")),
                                                wxString(_("Fermeture de ")) + f,wxYES_NO|(asCancel * wxCANCEL));
    int id = msg->ShowModal();

    if( id == wxID_CANCEL)
        return false;

    if( id == wxID_YES)
        mapManagerInstance.save(this);

    delete this;
    return true;
}

void Map::select( math::vector3q p, math::vector3a v)
{
    Model* nearest = NULL;
    float min = FLT_MAX;

    for(std::list<Model*>::iterator iter = models.begin(); iter != models.end(); iter++)
    {
        float f = (*iter)->getSqDistanceFromAxis( p, v);
        if( f < min)
        {
            min = f;
            nearest = *iter;
        }
    }

    if(!nearest)
        return;

    nearest->select();
    if( std::find(selectedModels.begin(), selectedModels.end(),nearest) == selectedModels.end())
        selectedModels.push_back(nearest);
}

void Map::unselect( math::vector3q p, math::vector3a v)
{
    std::list<Model*>::iterator nearest = selectedModels.end();
    float min = FLT_MAX;

    for(std::list<Model*>::iterator iter = selectedModels.begin(); iter != selectedModels.end(); iter++)
    {
        float f = (*iter)->getSqDistanceFromAxis( p, v);
        if( f < min)
        {
            min = f;
            nearest = iter;
        }
    }

    if(nearest == selectedModels.end())
        return;

    (*nearest)->unselect();
    selectedModels.erase(nearest);
}

void Map::unselectAll()
{
    std::for_each( selectedModels.begin(), selectedModels.end(), bind(&Model::unselect, _1));
    selectedModels.clear();
}

void Map::draw(bool noEffect)
{
    for(std::list<Model*>::iterator iter = models.begin(); iter != models.end(); iter++)
        (*iter)->draw(noEffect);

    //Redraw selected models behind the other
    for(std::list<Model*>::iterator iter = selectedModels.begin(); iter != selectedModels.end(); iter++)
        (*iter)->draw(noEffect);
}

void Map::loadWindowsManager()
{
    if( mgr)
        return;

    MapBasePane* pane = new MapBasePane(this, appInstance->m_frame->m_auiNotebook);
    mgr = new WindowsManager(pane);

    if( appInstance->m_useDefaultWindowsManager)
    {
        wxLogMessage(_("La configuration par défaut (fichier editor_layout) de la disposition de l’éditeur sera utilisé ( option -D utilisé)."));
        mgr->readConfigurationFromFile(wxT("editor_layout"));
    }
    else
    {
        LoadWindowsManagerRoutine* r = new LoadWindowsManagerRoutine;
        r->pane = pane;
        r->mgr = mgr;
        r->filename = filename;
        r->Create();
        r->Run();
    }

}
